![]() |
|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|
![]() |
![]() |
Java Design Patterns
SummaryBy David Geary
The Composite design pattern lets you treat primitive and composite objects exactly the same. In his latest Java Design Patterns column, David Geary explores how to implement the Composite pattern and how to use it with the Tiles tag library from the Apache Struts application framework. (2,800 words; September 13, 2002)
he other
day I was listening to National Public Radio's Car
Talk, a popular weekly broadcast during which callers ask questions
about their vehicles. Before every program break, the show's hosts ask callers
to dial 1-800-CAR-TALK, which corresponds to 1-800-227-8255. Of course, the
former proves much easier to remember than the latter, in part because the words
"CAR TALK" are a composite: two words that represent seven digits. Humans
generally find it easier to deal with composites, rather than their individual
components. Likewise, when you develop object-oriented software, it's often
convenient to manipulate composites just like you manipulate individual
components. That premise represents the fundamental principle of the Composite
design pattern, the topic of this Java Design Patterns
installment.
The Composite pattern
Before we dive
into the Composite pattern, I must first define composite objects:
objects that contain other objects; for example, a drawing may be composed of
graphic primitives, such as lines, circles, rectangles, text, and so on.
Java developers need the Composite pattern because we often must manipulate composites exactly the same way we manipulate primitive objects. For example, graphic primitives such as lines or text must be drawn, moved, and resized. But we also want to perform the same operation on composites, such as drawings, that are composed of those primitives. Ideally, we'd like to perform operations on both primitive objects and composites in exactly the same manner, without distinguishing between the two. If we must distinguish between primitive objects and composites to perform the same operations on those two types of objects, our code would become more complex and more difficult to implement, maintain, and extend.
In Design Patterns, the authors describe the Composite pattern like this:
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
Implementing the Composite pattern is easy. Composite classes extend a base class that represents primitive objects. Figure 1 shows a class diagram that illustrates the Composite pattern's structure.
Figure 1. A Composite pattern class diagram |
In Figure 1's class diagram, I used class names from Design
Pattern's Composite pattern discussion: Component
represents a
base class (or possibly an interface) for primitive objects, and
Composite
represents a composite class. For example, the
Component
class might represent a base class for graphic
primitives, whereas the Composite
class might represent a
Drawing
class. Figure 1's Leaf
class represents a
concrete primitive object; for example, a Line
class or a
Text
class. The Operation1()
and
Operation2()
methods represent domain-specific methods implemented
by both the Component
and Composite
classes.
The Composite
class maintains a collection of components.
Typically, Composite
methods are implemented by iterating over that
collection and invoking the appropriate method for each Component
in the collection. For example, a Drawing
class might implement its
draw()
method like this:
// This method is a Composite method
public void draw() {
// Iterate over the components
for(int i=0; i <
getComponentCount(); ++i) {
// Obtain a
reference to the component and invoke its draw
method
Component component =
getComponent(i);
component.draw();
}
}
For every method implemented in the Component
class, the
Composite
class implements a method with the same signature that
iterates over the composite's components, as illustrated by the
draw()
method listed above.
The Composite
class extends the Component
class, so
you can pass a composite to a method that expects a component; for example,
consider the following method:
// This method is implemented in a class that's unrelated to the
//
Component and Composite classes
public void repaint(Component component)
{
// The component can be a composite, but since it
extends
// the Component class, this method need
not
// distinguish between components and
composites
component.draw();
}
The preceding method is passed a component—either a simple component or a
composit—then it invokes that component's draw()
method. Because
the Composite
class extends Component
, the
repaint()
method need not distinguish between components and
composites—it simply invokes the draw()
method for the component
(or composite).
Figure 1's Composite pattern class diagram does illustrate one problem with
the pattern: you must distinguish between components and composites when you
reference a Component
, and you must invoke a composite-specific
method, such as addComponent()
. You typically fulfill that
requirement by adding a method, such as isComposite()
, to the
Component
class. That method returns false
for
components and is overridden in the Composite
class to return
true
. Additionally, you must also cast the Component
reference to a Composite
instance, like this:
...
if(component.isComposite()) {
Composite composite =
(Composite)component;
composite.addComponent(someComponentThatCouldBeAComposite);
}
...
Notice that the addComponent()
method is passed a
Component
reference, which can be either a primitive component or a
composite. Because that component can be a composite, you can compose components
into a tree structure, as indicated by the aforementioned quote from Design
Patterns.
Figure 2 shows an alternative Composite pattern implementation.
Figure 2. An alternative Composite pattern class
diagram |
If you implement Figure 2's Composite pattern, you don't ever have to
distinguish between components and composites, and you don't have to cast a
Component
reference to a Composite
instance. So the
code fragment listed above reduces to a single line:
...
component.addComponent(someComponentThatCouldBeAComposite);
...
But, if the Component
reference in the preceding code fragment
does not refer to a Composite
, what should the
addComponent()
do? That's a major point of contention with Figure
2's Composite pattern implementation. Because primitive components do not
contain other components, adding a component to another component makes no
sense, so the Component.addComponent()
method can either fail
silently or throw an exception. Typically, adding a component to another
primitive component is considered an error, so throwing an exception is perhaps
the best course of action.
So which Composite pattern implementation—the one in Figure 1 or the one in Figure 2—works best? That's always a topic of great debate among Composite pattern implementers; Design Patterns prefers the Figure 2 implementation because you never need to distinguish between components and containers, and you never need to perform a cast. Personally, I prefer Figure 1's implementation, because I have a strong aversion to implementing methods in a class that don't make sense for that object type.
Now that you understand the Composite pattern and how you can implement it, let's examine a Composite pattern example with the Apache Struts JavaServer Pages (JSP) framework.
The Composite pattern and Struts
Tiles
The Apache Struts framework includes a JSP tag library,
known as Tiles,
that lets you compose a Webpage from multiple JSPs. Tiles is actually an
implementation of the J2EE (Java 2 Platform, Enterprise Edition) CompositeView
pattern, itself based on the Design Patterns Composite pattern. Before
we discuss the Composite pattern's relevance to the Tiles tag library, let's
first review the rationale for Tiles, and how you use it. If you're already
familiar with Struts Tiles, you can skim the following sections and commence
reading at "Use the Composite Pattern with Struts Tiles."
Note: You can read more about the J2EE CompositeView pattern in my "Web Application Components Made Easy with Composite View" (JavaWorld, December 2001) article.
Designers often construct Webpages with a set of discrete regions; for example, Figure 3's Webpage comprises a sidebar, header, content region, and footer.
Figure 3. The Composite pattern and Struts Tiles. Click on thumbnail to
view full-size image. |
Websites often include multiple Webpages with identical layouts, such as Figure 3's sidebar/header/content/footer layout. Struts Tiles lets you reuse both content and layout among multiple Webpages. Before we discuss that reuse, let's see how Figure 3's layout is traditionally implemented with HTML alone.
Implement complex layouts by hand
Example 1 shows how you
can implement Figure 3's Webpage with HTML:
Example 1. A complex layout implemented by hand <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0
Transitional//EN">
<%@ page contentType='text/html; charset=UTF-8'
%>
<html>
<head>
<title>Implementing
Complex Layouts by Hand</title>
</head>
<body
background='graphics/blueAndWhiteBackground.gif'>
<%--
One table lays out all of the content for this page
--%>
<table width='100%'
height='100%'>
<tr>
<%--
Sidebar section
--%>
<td
width='150' valign='top'
align='left'>
<table>
<tr>
<%-- Sidebar top
--%>
<td width='150' height='65' valign='top'
align='left'>
<a
href=''>
<img
src='graphics/flags/britain_flag.gif'/></a>
<a
href=''>
<img
src='graphics/flags/german_flag.gif'/></a>
<a
href=''>
<img
src='graphics/flags/chinese_flag.gif'/></a>
</td>
</tr>
<tr>
<%-- Sidebar bottom
--%>
<td>
<font
size='5'>Links</font><p>
<a
href=''>Home</a><br>
<a
href=''>Products</a><br>
<a
href=''>Downloads</a><br>
<a
href=''>White
papers</a><br>
<a
href=''>Contact
us</a><br>
</td>
</tr>
</table>
</td>
<%--
Main content section
--%>
<td
valign='top' height='100%'
width='*'>
<table width='100%'
height='100%'>
<tr>
<%-- Header section
--%>
<td valign='top'
height='15%'>
<font
size='6'>Welcome to Sabreware,
Inc.</font>
<hr>
</td>
<tr>
<tr>
<%-- Content section
--%>
<td valign='top'
height='*'>
<font
size='4'>Page-specific content goes
here</font>
</td>
</tr>
<tr>
<%-- Footer section
--%>
<td valign='bottom'
height='15%'>
<hr>
Thanks
for stopping
by!
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
The preceding JSP has two major drawbacks: First, the page's content is
embedded in the JSP, so you cannot reuse any of it, even though the sidebar,
header, and footer are likely to be the same across many Webpages. Second, the
page's layout is also embedded in that JSP, so you likewise cannot reuse it even
though many other Webpages in the same Website use the same layout. We can use
the <jsp:include>
action to remedy the first drawback, as I
discuss next.
Implement complex layouts with JSP includes
Example 2
shows an implementation of Figure 3's Webpage that uses
<jsp:include>
:
Example 2. A complex layout implemented with JSP
includes <%@ page contentType='text/html; charset=UTF-8'
%>
<html>
<head>
<title>Implementing
Complex Layouts by Hand</title>
</head>
<body
background='graphics/blueAndWhiteBackground.gif'>
<%--
One table lays out all of the content for this page
--%>
<table width='100%'
height='100%'>
<tr>
<%--
Sidebar section
--%>
<td
width='150' valign='top'
align='left'>
<jsp:include
page='sidebar.jsp'/>
</td>
<%--
Main content section
--%>
<td
height='100%'
width='*'>
<table width='100%'
height='100%'>
<tr>
<%-- Header section
--%>
<td valign='top'
height='15%'>
<jsp:include
page='header.jsp'/>
</td>
<tr>
<tr>
<%-- Content section
--%>
<td valign='top'
height='*'>
<jsp:include
page='content.jsp'/>
</td>
</tr>
<tr>
<%-- Footer section
--%>
<td valign='bottom'
height='15%'>
<jsp:include
page='footer.jsp'/>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
The preceding JSP includes the content of other JSPs with
<jsp:include>
. Because I've encapsulated that content in
separate JSPs, you can reuse it for other Webpages:
Example 3. sidebar.jsp <%@ page contentType='text/html; charset=UTF-8' %>
<table
width='100%'>
<tr>
<%-- Sidebar top component
--%>
<td width='150' height='65'
valign='top'
align='left'>
<a
href=''><img
src='graphics/flags/britain_flag.gif'/></a>
<a
href=''><img
src='graphics/flags/german_flag.gif'/></a>
<a
href=''><img
src='graphics/flags/chinese_flag.gif'/></a>
</td>
</tr>
<tr>
<%-- Sidebar bottom
component
--%>
<td>
<table>
<tr>
<td>
<font
size='5'>Links</font><p>
<a
href=''>Home</a><br>
<a
href=''>Products</a><br>
<a
href=''>Downloads</a><br>
<a
href=''>White
papers</a><br>
<a
href=''>Contact
us</a><br>
</td>
</tr>
</table>
</td>
</tr>
</table>
For completeness, I've listed below the JSPs included by the preceding JSP:
Example 4. header.jsp <font size='6'>Welcome to Sabreware,
Inc.</font>
<hr>
Example 5. content.jsp <font size='4'>Page-specific content goes here</font>
Example 6. footer.jsp <hr>
Thanks for stopping by!
Even though Example 2's JSP uses <jsp:include>
to reuse
content, you can't reuse the page's layout because it's
hardcoded in that JSP. Struts Tiles lets you reuse both the content and the
layout, as illustrated in the next section.
Implement complex layouts with Struts Tiles
Example 7
shows the Webpage in Figure 3 implemented with Struts Tiles:
Example 7. Use Struts Tiles to encapsulate layout <%@ page contentType='text/html; charset=UTF-8' %>
<%@ taglib
uri='WEB-INF/tlds/struts-tiles.tld' prefix='tiles' %>
<tiles:insert
definition='sidebar-header-footer-definition'/>
The preceding JSP uses the <tiles:insert>
tag to create
Figure 3's JSP. That JSP is defined by a tiles definition named
sidebar-header-footer-definition
. That definition resides in the
Tiles configuration file, which in this case is
WEB-INF/tiles-defs.xml
, listed in Example 8:
Example 8. WEB-INF/tiles-defs.xml <!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software
Foundation//DTD Tiles
Configuration//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config.dtd">
<tiles-definitions>
<definition name='sidebar-header-footer-definition'
path='header-footer-sidebar-layout.jsp'>
<put
name='sidebar'
value='sidebar.jsp'/>
<put
name='header' value='header.jsp'/>
<put name='content'
value='content.jsp'/>
<put
name='footer' value='footer.jsp'/>
</definition>
</tiles-definitions>
The preceding Tiles definition specifies the page layout, encapsulated in
header-footer-sidebar-layout.jsp
, and the page's content,
encapsulated in sidebar.jsp
, header.jsp
,
content.jsp
, and footer.jsp
, as listed in Examples
3-6. Example 9 lists the JSP that defines the
layout—header-footer-sidebar-layout.jsp
:
Example 9. header-footer-sidebar-layout.jsp <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0
Transitional//EN">
<%@ page contentType='text/html; charset=UTF-8'
%>
<html>
<head>
<title>Struts Tiles
implements the Composite pattern</title>
</head>
<body
background='graphics/blueAndWhiteBackground.gif'>
<%@
taglib uri='/WEB-INF/tlds/struts-tiles.tld'
prefix='tiles'%>
<%--
One table lays out all of the content
--%>
<table width='100%'
height='100%'>
<%--
Sidebar section --%>
<tr>
<td
width='150' valign='top'
align='left'>
<tiles:insert
attribute='sidebar'/>
</td>
<%--
Main content section
--%>
<td
valign='top' height='100%'
width='*'>
<table width='100%'
height='100%'>
<tr>
<%-- Header section
--%>
<td
height='15%'>
<tiles:insert
attribute='header'/>
</td>
<tr>
<tr>
<%-- Content section
--%>
<td valign='top'
height='*'>
<tiles:insert
attribute='content'/>
</td>
</tr>
<tr>
<%-- Footer section
--%>
<td valign='bottom'
height='15%'>
<tiles:insert
attribute='footer'/>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
The preceding JSP encapsulates layout and inserts content according to the values specified for the sidebar, editor, content, and footer regions in the Tiles definition file, thus easing reuse for both the content and the layout. For example, you could define another Tiles definition with the same layout, the same sidebar, editor, and footer, but different content:
<tiles-definitions>
<definition name='a-different-sidebar-header-footer-definition'
path='header-footer-sidebar-layout.jsp'>
<put
name='sidebar'
value='sidebar.jsp'/>
<put
name='header' value='header.jsp'/>
<put name='content'
value='someOtherContent.jsp'/>
<put
name='footer' value='footer.jsp'/>
</definition>
</tiles-definitions>
To create the JSP defined by the tiles definition
a-different-sidebar-header-footer-definition
, you use the
<tiles:insert>
tag, like this:
<%@ page contentType='text/html; charset=UTF-8' %>
<%@ taglib
uri='WEB-INF/tlds/struts-tiles.tld' prefix='tiles' %>
<tiles:insert
definition='a-different-sidebar-header-footer-definition'/>
Thanks to Struts Tiles, you can reuse both content and layout, which proves
invaluable for Websites with many JSPs that share layout and some content. But
if you look closely at the code from Examples 7-9, you will notice that the
layout for the sidebar region is hardcoded in sidebar.jsp
, which is
listed in Example 3. That means you cannot reuse that layout. Fortunately, the
Tiles tag library implements the Composite pattern, which lets us specify a
tiles definition—instead of a JSP—for a region. In the next section, I explain
how to use that Composite pattern implementation.
Use the Composite pattern with Struts Tiles
Struts Tiles
implements the Composite pattern, where the Component
class is
represented by JSPs and the Composite
class is represented by a
Tiles definition. That implementation lets you specify either a JSP (a
component) or a Tiles definition (a composite) as the content for a JSP's
region. Example 10 illustrates that feature:
Example 10. WEB-INF/tiles-defs.xml: Use the Composite
pattern <!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software
Foundation//DTD Tiles
Configuration//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config.dtd">
<tiles-definitions>
<definition name='sidebar-definition'
path='sidebar-layout.jsp'>
<put
name='top' value='flags.jsp'/>
<put
name='bottom' value='sidebar-links.jsp'/>
</definition>
<definition name='sidebar-header-footer-definition'
path='header-footer-sidebar-layout.jsp'>
<put
name='sidebar'
value='sidebar-definition'
type='definition'/>
<put
name='header' value='header.jsp'/>
<put
name='content'
value='content.jsp'/>
<put
name='footer' value='footer.jsp'/>
</definition>
</tiles-definitions>
The preceding Tiles configuration file defines two Tiles definitions:
sidebar-definition
and
sidebar-header-footer-definition
. The
sidebar-definition
is specified as the value for the sidebar
region in the sidebar-header-footer-definition
. You can
specify it as such because Tiles implements the Composite pattern by letting
Tiles specify a definition (a Composite
that's a JSP collection)
where you would normally specify a single JSP (which is a
Component
).
The sidebar's layout is encapsulated in Example 11's
sidebar-layout.jsp
:
Example 11. sidebar-layout.jsp <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0
Transitional//EN">
<%@ page contentType='text/html; charset=UTF-8'
%>
<%@ taglib uri='/WEB-INF/tlds/struts-tiles.tld'
prefix='tiles'%>
<table width='100%'>
<tr>
<td width='150' height='65'
valign='top'
align='left'>
<tiles:insert
attribute='top'/>
</td>
</tr>
<tr>
<td>
<table>
<tr>
<td><tiles:insert
attribute='bottom'/></td>
</tr>
</table>
</td>
</tr>
</table>
Example 12 lists flags.jsp
, specified as the content for the
sidebar's top
region, and Example 13 lists
sidebar-links.jsp
, specified as the sidebar's bottom
region:
Example 12. flags.jsp <table>
<tr><td>
<a
href=''><img
src='graphics/flags/britain_flag.gif'/></a>
<a
href=''><img
src='graphics/flags/german_flag.gif'/></a>
<a
href=''><img
src='graphics/flags/chinese_flag.gif'/></a>
</td></tr>
</table>
Example 13. sidebar-links.jsp <p>
<font size='5'>Links</font><p>
<a
href=''>Home</a><br>
<a
href=''>Products</a><br>
<a
href=''>Downloads</a><br>
<a href=''>White
papers</a><br>
<a href=''>Contact
us</a><br>
Now the sidebar-definition
can define other regions with a top
and bottom component, although you should probably rename that definition to
something more generic like top-bottom-definition
.
It's all composites these days
The
Composite pattern is popular with presentation frameworks, such as Swing and
Struts, because it lets you nest containers by treating components and their
containers exactly the same. Struts Tiles uses the Composite pattern to specify
a simple JSP or a Tiles definition—which is a collection of JSPs—as a tile's
content. That's a powerful capability that eases management of large Websites
with different layouts.
The "Homework from Last Time" section below expands on this article's discussion by internationalizing the preceding application with a Struts action and the JSP Standard Tag Library (JSTL).
Homework
Discuss how Swing
implements the composite pattern with the Component
and
Container
classes.
Homework from last time
Your last
assignment asked you to download Struts from http://jakarta.apache.org/struts/index.html
and implement your own Struts action class.
For this assignment, I decided to implement a Struts action in conjunction with the JSP Standard Tag Library (JSTL) to internationalize Example 1's Web application. Although Struts provides the necessary infrastructure to internationalize your Web applications, you should use JSTL for that task, because JSTL is a standard. At some point, the Struts internationalization capabilities will probably be deprecated or integrated with JSTL.
After I internationalized Example 1's Web application with a Struts action and JSTL, I localized that application for Chinese. Figure H1 illustrates the result.
Note: I don't know a single word of Chinese, let alone how to write the language, so Figure H1's Chinese is fabricated from arbitrary Unicode strings. But it looks cool, regardless.
Figure H1. Internationalization with a Struts Action. Click on
thumbnail to view full-size image. |
Notice I specified the href
attributes in Example 12's
flags.jsp
as empty strings, so when you click on the flags, the
servlet container reloads the current JSP. Therefore, the first step towards
internationalizing the Web application is to specify a URL for those attributes,
as listed in Example H1:
Example H1. flags.jsp <table>
<tr><td>
<a
href='flags.do?locale=en-GB&fwdPage=<%=request.getServletPath()%>'>
<img
src='graphics/flags/britain_flag.gif'/></a>
<a
href='flags.do?locale=de-DE&fwdPage=<%=request.getServletPath()%>'>
<img
src='graphics/flags/german_flag.gif'/></a>
<a
href='flags.do?locale=zh-ZH&fwdPage=<%=request.getServletPath()%>'>
<img src='graphics/flags/chinese_flag.gif'/></a>
</td></tr>
</table>
The URL for each href
attribute is the same:
flags.do
. Two request parameters are tacked onto that URL: one for
the locale corresponding to the flag and another that represents the current
JSP's path. You obtain the latter request parameter by invoking the
Http.ServletRequest.getServletPath()
method.
In the application's deployment descriptor, I mapped all URLs ending in
.do
to the Struts action servlet, like this:
Example H2. WEB-INF/web.xml (Excerpt) <?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE
web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<!-- Action Servlet Configuration -->
<servlet>
<servlet-name>
action
</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
...
<load-on-startup/>
</servlet>
<!-- Action Servlet Mapping
-->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
...
</web-app>
Note: See my "Take Command of Your Software" (JavaWorld, June 2002) article for more information about Struts and the Struts action servlet.
Next, I mapped the path /flags
to the Struts action
actions.FlagAction
in the Struts configuration file, listed in
Example H3:
Example H3. WEB-INF/struts-config.xml <?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE
struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts
Configuration
1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<action-mappings>
<action
path='/flags' type='actions.FlagAction'/>
</action-mappings>
<plug-in
className="org.apache.struts.tiles.TilesPlugin"
>
<set-property
property="definitions-config"
value="/WEB-INF/tiles-defs.xml"
/>
<set-property
property="definitions-debug" value="2"
/>
<set-property
property="definitions-parser-details"
value="2"
/>
<set-property
property="definitions-parser-validate"
value="true" />
</plug-in>
</struts-config>
Because of that mapping, the URL flags.do
causes the Struts
action servlet to invoke the actions.FlagAction.execute()
method;
therefore, clicking on a flag will invoke the
actions.FlagAction.execute()
method. Example H4 lists the
actions.FlagAction
class:
Example H4.
WEB-INF/classes/actions/FlagAction.java package actions;
import javax.servlet.ServletContext;
import
javax.servlet.http.*;
import org.apache.struts.action.*;
import
javax.servlet.jsp.jstl.core.Config;
public class FlagAction extends
Action {
public ActionForward execute(ActionMapping mapping,
ActionForm
form,
HttpServletRequest
request,
HttpServletResponse
response)
throws java.io.IOException, javax.servlet.ServletException
{
String locale =
request.getParameter("locale");
Config.set(getServlet().getServletContext(),
Config.FMT_LOCALE,
locale);
return new
ActionForward(request.getParameter("fwdPage"));
}
}
The actions.FlagAction.execute()
method obtains a reference to
the locale
request parameter and passes that value to the JSTL
Config
class's set()
method. That method stores the
string representing the locale in the FMT_LOCALE
configuration
setting, which JSTL uses to localize text and format numbers, currencies,
percents, and dates.
Now that I've specified a locale for JSTL internationalization actions, I next specify a resource bundle in the application's deployment descriptor, an excerpt of which is listed in Example H5:
Example H5. WEB-INF/web.xml (Excerpt) <?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE
web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app>
<!-- Define the basename for a resource bundle for I18N
-->
<context-param>
<param-name>
javax.servlet.jsp.jstl.fmt.localizationContext
</param-name>
<param-value>
resources
</param-value>
</context-param>
...
</web-app>
The resource bundle basename resources
is specified for
the javax.servlet.jsp.jstl.fmt.localizationContext
context-initialization parameter. That means JSTL will search for a file named
resources_en.properties
when the locale is set to British
English (en-GB
) and resources_zh.properties
when the locale is set to Chinese (zh-ZH
).
Now that I've specified a resource bundle and a locale for the JSTL internationalization actions, I next modify the JSP files to use those actions, as Examples H6-H9 demonstrate:
Example H6. sidebar-links.jsp <%@ page contentType='text/html; charset=UTF-8' %>
<%@ taglib
uri='http://java.sun.com/jstl/fmt' prefix='fmt'
%>
<p>
<font size='5'><fmt:message
key='sidebar.heading'/></font><p>
<a
href='index.jsp'><fmt:message
key='sidebar.link.1.name'/></a><br>
<a
href='products.jsp'><fmt:message
key='sidebar.link.2.name'/></a><br>
<a
href='downloads.jsp'><fmt:message
key='sidebar.link.3.name'/></a><br>
<a
href='sales.jsp'><fmt:message
key='sidebar.link.4.name'/></a><br>
<a
href='contact.jsp'><fmt:message
key='sidebar.link.5.name'/></a><br>
Example H7. header.jsp <%@ page contentType='text/html; charset=UTF-8' %>
<%@ taglib
uri='http://java.sun.com/jstl/fmt' prefix='fmt' %>
<font
size='6'><fmt:message
key='header.heading'/></font>
<hr>
Example H8. content.jsp <%@ page contentType='text/html; charset=UTF-8' %>
<%@ taglib
uri='http://java.sun.com/jstl/fmt' prefix='fmt' %>
<font
size='4'><fmt:message key='content.heading'/></font>
Example H9. footer.jsp <%@ page contentType='text/html; charset=UTF-8' %>
<%@ taglib
uri='http://java.sun.com/jstl/fmt' prefix='fmt'
%>
<hr>
<fmt:message key='footer.message'/>
Examples H6-H9's JSPs use the JSTL <fmt:message>
action to
extract Unicode strings from the resource bundle specified in the deployment
descriptor. Examples H10 and H11 list the resource bundles for English and
Chinese, respectively:
Example H10.
WEB-INF/classes/resources_en.properties header.heading=Welcome to Sabreware, Inc.
content.heading=Page-specific
content goes here
footer.message=Thanks for stopping
by!
sidebar.heading=Links
sidebar.link.1.name=Home
sidebar.link.2.name=Products
sidebar.link.3.name=Downloads
sidebar.link.4.name=Sales
sidebar.link.5.name=Contact
Us
Example H11.
WEB-INF/classes/resources_zh.properties header.heading=\u5bc6\u7801\u63d0\u793a\u95ee\u9898 Sabreware,
Inc.
content.heading=\u6709\u6548\u671f\u9650
footer.message=\u4eca\u5929\u662f
sidebar.heading=\u8fd9\u91cc
sidebar.link.1.name=\u767b\u5f55
sidebar.link.2.name=\u540d
sidebar.link.3.name=\u59d3
sidebar.link.4.name=\u7701\u4efd
sidebar.link.5.name=\u7501\u4cfe
Email
In an email to me, Joseph
Friedman wrote:
In "Decorate Your Java Code" (JavaWorld, December 2001), you write:
"The enclosing object—known as a decorator—conforms to the interface of the object it encloses, allowing the decorator to be used as though it were an instance of the object it encloses."
However, the code example decorates the
FileReader
with aLineNumberReader
and callsreadLine()
, which is not in theFileReader
interface; thus you are not using theLineNumberReader
transparently.
Both FileReader
and LineNumberReader
conform to the
interface defined by the Reader
class, which they both extend. The
idea is that you can pass the decorator to any method that expects a reference
to a Reader
. Those methods remain blissfully ignorant of that
decorator's special capabilities—in this case, the ability to read files and
produce line numbers. However, if you know about those special capabilities, you
can take advantage of them.
The fact that the decorator possesses (one or more) methods that the decorated object lacks does not in any way violate the Decorator pattern's intent; in fact, that's the central feature of that pattern: to add functionality at runtime to an object by decorating objects recursively.
If you look at Design Patterns page 173, you will see this:
Decorator subclasses are free to add operations for specific functionality. For example,
ScrollDecorator
'sScrollTo
operation lets other objects scroll the interface *if* they know there happens to be aScrollDecorator
object in the interface.
About the author
David Geary is the
author of Core JSTL
Mastering the JSP Standard Tag Library, which will be published this
fall by Prentice-Hall and Sun Microsystems Press; Advanced JavaServer Pages (Prentice Hall, 2001; ISBN:
0130307041); and the Graphic Java series (Sun Microsystems Press). David
has been developing object-oriented software with numerous object-oriented
languages for 18 years. Since the GOF Design Patterns book was
published in 1994, David has been an active proponent of design patterns, and
has used and implemented design patterns in Smalltalk, C++, and Java. In 1997,
David began working full-time as an author and occasional speaker and
consultant. David is a member of the expert groups defining the JSP standard
custom tag library and JavaServer Faces, and is a contributor to the Apache
Struts JSP framework.
![]() |
![]() |
|
![]() |
Copyright © 2002 JavaWorld.com, an IDG Communications company |
![]() |
![]() |
![]() |